/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.interceptors;

import org.omg.PortableInterceptor.Interceptor;
import org.omg.PortableInterceptor.ORBInitInfo;
import org.omg.PortableInterceptor.ORBInitInfoPackage.DuplicateName;

import org.omg.CORBA.INTERNAL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.lang.reflect.Array;

import com.sun.corba.se.impl.logging.InterceptorsSystemException ;

/** 
 * Provides a repository of registered Portable Interceptors, organized
 * by type.  This list is designed to be accessed as efficiently as 
 * possible during runtime, with the expense of added complexity during
 * initialization and interceptor registration.  The class is designed
 * to easily allow for the addition of new interceptor types.
 */
public class InterceptorList {

    // Interceptor type list.  If additional interceptors are needed,
    // add additional types in numerical order (do not skip numbers),
    // and update NUM_INTERCEPTOR_TYPES and classTypes accordingly.
    // NUM_INTERCEPTOR_TYPES represents the number of interceptor 
    // types, so we know how many lists to maintain.
    static final int INTERCEPTOR_TYPE_CLIENT 		= 0;
    static final int INTERCEPTOR_TYPE_SERVER 		= 1;
    static final int INTERCEPTOR_TYPE_IOR    		= 2;
    
    static final int NUM_INTERCEPTOR_TYPES    		= 3;
    
    // Array of class types for interceptors.  This is used to create the
    // appropriate array type for each interceptor type.  These must 
    // match the indices of the constants declared above.
    static final Class[] classTypes = {
        org.omg.PortableInterceptor.ClientRequestInterceptor.class,
        org.omg.PortableInterceptor.ServerRequestInterceptor.class,
        org.omg.PortableInterceptor.IORInterceptor.class
    };
    
    // True if no further interceptors may be registered with this list.
    private boolean locked = false;
    private InterceptorsSystemException wrapper ;

    // List of interceptors currently registered.  There are 
    // NUM_INTERCEPTOR_TYPES lists of registered interceptors.
    // For example, interceptors[INTERCEPTOR_TYPE_CLIENT] contains an array
    // of objects of type ClientRequestInterceptor.
    private Interceptor[][] interceptors = 
        new Interceptor[NUM_INTERCEPTOR_TYPES][];
   
    /**
     * Creates a new Interceptor List.  Constructor is package scope so
     * only the ORB can create it.
     */
    InterceptorList( InterceptorsSystemException wrapper ) {
	this.wrapper = wrapper ;
        // Create empty interceptors arrays for each type:
        initInterceptorArrays();
    }

    /**
     * Registers an interceptor of the given type into the interceptor list.
     * The type is one of:
     * <ul>
     *   <li>INTERCEPTOR_TYPE_CLIENT - ClientRequestInterceptor
     *   <li>INTERCEPTOR_TYPE_SERVER - ServerRequestInterceptor
     *   <li>INTERCEPTOR_TYPE_IOR - IORInterceptor
     * </ul>
     *
     * @exception DuplicateName Thrown if an interceptor of the given
     *     name already exists for the given type.
     */
    void register_interceptor( Interceptor interceptor, int type ) 
	throws DuplicateName
    {
        // If locked, deny any further addition of interceptors.
        if( locked ) {
	    throw wrapper.interceptorListLocked() ;
        }
        
	// Cache interceptor name:
	String interceptorName = interceptor.name();
	boolean anonymous = interceptorName.equals( "" );
	boolean foundDuplicate = false;
	Interceptor[] interceptorList = interceptors[type];

	// If this is not an anonymous interceptor, 
	// search for an interceptor of the same name in this category:
	if( !anonymous ) {
	    int size = interceptorList.length;

	    // An O(n) search will suffice because register_interceptor is not
	    // likely to be called often.
	    for( int i = 0; i < size; i++ ) {
		Interceptor in = (Interceptor)interceptorList[i];
		if( in.name().equals( interceptorName ) ) {
		    foundDuplicate = true;
		    break;
		}
	    }
	}

	if( !foundDuplicate ) {
            growInterceptorArray( type );
            interceptors[type][interceptors[type].length-1] = interceptor;
	}
	else {
	    throw new DuplicateName( interceptorName );
	}
    }

    /**
     * Locks this interceptor list so that no more interceptors may be
     * registered.  This method is called after all interceptors are
     * registered for security reasons.
     */
    void lock() {
        locked = true;
    }
    
    /**
     * Retrieves an array of interceptors of the given type.  For efficiency,
     * the type parameter is assumed to be valid.
     */
    Interceptor[] getInterceptors( int type ) {
        return interceptors[type];
    }

    /**
     * Returns true if there is at least one interceptor of the given type,
     * or false if not.
     */
    boolean hasInterceptorsOfType( int type ) {
	return interceptors[type].length > 0;
    }
    
    /**
     * Initializes all interceptors arrays to zero-length arrays of the
     * correct type, based on the classTypes list.
     */
    private void initInterceptorArrays() {
        for( int type = 0; type < NUM_INTERCEPTOR_TYPES; type++ ) {
            Class classType = classTypes[type];
            
            // Create a zero-length array for each type:
            interceptors[type] = 
                (Interceptor[])Array.newInstance( classType, 0 );
        }
    }
    
    /**
     * Grows the given interceptor array by one:
     */
    private void growInterceptorArray( int type ) {
        Class classType = classTypes[type];
        int currentLength = interceptors[type].length;
        Interceptor[] replacementArray;
        
        // Create new array to replace the old one.  The new array will be
        // one element larger but have the same type as the old one.
        replacementArray = (Interceptor[])
            Array.newInstance( classType, currentLength + 1 );
        System.arraycopy( interceptors[type], 0,
                          replacementArray, 0, currentLength );
        interceptors[type] = replacementArray;
    }

    /**
     * Destroys all interceptors in this list by invoking their destroy()
     * method.
     */
    void destroyAll() {
	int numTypes = interceptors.length;

	for( int i = 0; i < numTypes; i++ ) {
	    int numInterceptors = interceptors[i].length;
	    for( int j = 0; j < numInterceptors; j++ ) {
		interceptors[i][j].destroy();
	    }
	}
    }

    /**
     * Sort interceptors.
     */
    void sortInterceptors() {
	List sorted = null;
	List unsorted = null;

	int numTypes = interceptors.length;

	for( int i = 0; i < numTypes; i++ ) {
	    int numInterceptors = interceptors[i].length;
	    if (numInterceptors > 0) {
		// Get fresh sorting bins for each non empty type.
		sorted = new ArrayList(); // not synchronized like we want.
		unsorted = new ArrayList();
	    }
	    for( int j = 0; j < numInterceptors; j++ ) {
		Interceptor interceptor = interceptors[i][j];
		if (interceptor instanceof Comparable) {
		    sorted.add(interceptor);
		} else {
		    unsorted.add(interceptor);
		}
	    }
	    if (numInterceptors > 0 && sorted.size() > 0) {
		// Let the RuntimeExceptions thrown by sort
		// (i.e., ClassCastException and UnsupportedOperationException)
		// flow back to the user.
		Collections.sort(sorted);
		Iterator sortedIterator = sorted.iterator();
		Iterator unsortedIterator = unsorted.iterator();
		for( int j = 0; j < numInterceptors; j++ ) {
		    if (sortedIterator.hasNext()) {
			interceptors[i][j] =
			    (Interceptor) sortedIterator.next();
		    } else if (unsortedIterator.hasNext()) {
			interceptors[i][j] =
			    (Interceptor) unsortedIterator.next();
		    } else {
			throw wrapper.sortSizeMismatch() ;
		    }
		}
	    }
	}
    }
}

